#include "threadPool.h"

//注册程序
int registerUser(int netfd, MYSQL *mysql, char *userName, char *passWord)
{
    char salt[21] = {0};
    char *encrypted = NULL;
    randomSaltValue(salt);//生成随机盐值函数
    printf("salt = %s\n", salt);
    encrypted = crypt(passWord, salt);//生成密文
    if(encrypted == NULL)
    {
        printf("加密失败，注册失败。\n");
        return -1;
    }
    printf("encrypted = %s\n", encrypted);//打印密文
    char sql[1024];

    userName[strcspn(userName, "\n")] = '\0';
    salt[strcspn(salt, "\n")] = '\0';
    encrypted[strcspn(encrypted, "\n")] = '\0';

    snprintf(sql, sizeof(sql), "insert into user_excel (username, salt, encrypted_password, is_deleted) values ('%s', '%s', '%s', 0);", userName, salt, encrypted);

    if(mysql_query(mysql, sql) == 0)
    {
        send(netfd, "0", 2, MSG_NOSIGNAL);
        printf("用户%s注册成功。\n", userName);
        func_mkdir(userName,NULL, "/", mysql);
    }
    else
    {
        send(netfd, "-1", 3, MSG_NOSIGNAL);
        fprintf(stderr, "注册失败：%s\n", mysql_error(mysql));
    }
    return 0;
}


//登录程序
int userLogin(int netfd, MYSQL *mysql, char *userName)
{
    // char userName[1024] = {0};
    //根据用户名查询盐值和密文
    char sql[1024];
    userName[strcspn(userName, "\n")] = '\0';
    snprintf(sql, sizeof(sql), "select salt ,encrypted_password from user_excel where username = '%s' and is_deleted=0;",userName);
    if(mysql_query(mysql, sql) != 0)
    {
        //send(netfd, "查询失败。\n");
        printf("查无此人。\n");
        return -1;
    }
    //查询成功，此时有张表
    MYSQL_RES *res = mysql_store_result(mysql);//读表格
    MYSQL_ROW row = mysql_fetch_row(res);

    if(!row)
    {
        //send(netfd, "-1", 2, 0);
        mysql_free_result(res);
        return -1;
    }
    char *salt = row[0];
    char *encrypted = row[1];
    printf("salt=%s",salt);
    //发送盐值给client
    send(netfd, salt, strlen(salt), MSG_NOSIGNAL);

    //等待客户端密文
    char encryptedInput[512] = {0};
    ssize_t ret = recv(netfd, encryptedInput, sizeof(encryptedInput)-1, MSG_NOSIGNAL);
    if(ret <= 0)
    {
        //send(netfd, "-1", 2, 0);
        printf("密文接收失败。\n");
        return -1;
    }
    encryptedInput[ret] = '\0';

    //比对密文
    if(strcmp(encryptedInput, encrypted) == 0)
    {
        send(netfd, "0", 2, MSG_NOSIGNAL);
        printf("客户端%s登陆成功。\n",userName);
        mysql_free_result(res);
        return 0;
    }
    else
    {
        send(netfd, "-1", 3, MSG_NOSIGNAL);
        printf("客户端%s登陆失败。\n", userName);
        mysql_free_result(res);
        return -1;
    }
}


void * worker (void * arg)
{
    Package * package=(Package *)arg;
    //执行相应操作
    if(package->flag==3)//一个是puts一个是gets
    {
        //0是puts,客户端上传，调用函数
        //接受hashValue
        char hashValue[49]={0};
        train_t train;
        memset(&train,0,sizeof(train));
        bzero(0,sizeof(hashValue));
        recv(package->netfd, &train.length, sizeof(train.length), MSG_WAITALL);
        recv(package->netfd, hashValue, train.length, MSG_WAITALL);                                              
        printf("%s,len=%ld\n",hashValue,strlen(hashValue));//打印
                                                           //hashValue[train.length] = '\0';
        hashValue[strcspn(hashValue, "\n")] = '\0';
        hashValue[strcspn(hashValue, "\t")] = '\0';
        ssize_t ret = quick_send(hashValue,package->mysql);//验证是否触发秒传
        if(ret == 0)
        {
            printf("文件已存在。\n");
            send(package->netfd, "0", 2, MSG_NOSIGNAL);

        }
        else//文件不存在，要上传
        {
            printf("正在下载文件...\n");
            ssize_t ret = recv_file(&package->stack, hashValue,package-> userName, package->netfd,package->mysql);
            printf ("我是worker,我接受文件完成");
            if(ret == 0)
            {
                send(package->netfd, "0", 2, MSG_NOSIGNAL);
                ssize_t ret = check_file_full(&package->stack, hashValue, package->fileName,package->mysql);//上传完校验文件
                if(ret == 0)
                {
                    send(package->netfd, "0", 2, MSG_NOSIGNAL);
                }
                else
                {
                    send(package->netfd, "-1", 3, MSG_NOSIGNAL);
                }
            }
            else
            {
                send(package->netfd, "-1", 3, MSG_NOSIGNAL);
            }
        }
    }
    else
    {//此时是gets，客户端下载，调用函数
        ssize_t ret =func_puts(package->netfd,package->fileName,&package->stack,package->mysql,package->userName);
        if(ret == 0)
        {
            send(package->netfd, "0", 2, MSG_NOSIGNAL);
        }   
        else
        {
            send(package->netfd, "-1", 3, MSG_NOSIGNAL);
        }   

    }
    return NULL;
}




int main(int argc,char* argv[])//master进程
{
    //客户端标准初始化流程
    int workerNum=atoi(argv[3]);
    printf("workNum is %d\n",workerNum);
    int sockfd=initTcp(argv[1],argv[2]);
    int epfd=epoll_create(1);
    epollAdd(epfd,sockfd);
    struct epoll_event readyset[50];
    printf("客户端标准初始化完成\n");
    //hash 初始化，可以根据netfd查找在队列下标和用户名
    hash_t hash[1024];
    for(int i=0;i<1024;i++)
    {
        hash[i].flag=-1;
        hash[i].queue_location=0;
        bzero(hash[i].userName,sizeof(hash[i].userName));
    }
    printf("hash初始化完成\n");
    //循环队列初始化
    Set queue[30];//0-29
    int queue_index=-1;
    for(int i=0;i<30;i++)set_init(&queue[i]);//队列中每个元素初始化，一个元素是一个集合
    printf("循环队列初始化完成\n");
    MYSQL *mysql=mysql_init(NULL);
    MYSQL *ret= mysql_real_connect(mysql,
                                   "localhost",
                                   "root",
                                   "li123",
                                   "wp_v3",
                                   0,NULL,0);
    if(ret==NULL)
    {
        printf("数据库连接失败！\n");
        exit(-1);
    }
    printf("数据库连接成功！");

    //执行while
    while(1)
    {
        int ready_len=epoll_wait(epfd,readyset,50,1000);//1s时返回
        if(ready_len!=0)sleep(1);
        queue_index++;
        queue_index%=30;
        printf("now queue_index = %d\n",queue_index);

        int overtime_num=queue[queue_index].set_index;
        printf("当前循环队列有%d个fd应该断开\n",overtime_num);
        set_print(&queue[queue_index]);
        for(int j=0;j<overtime_num;j++)
        {
            int overtime_fd=queue[queue_index].elements[j];
            printf("目前解决的overtime_fd是%d\n",overtime_fd);
            send(overtime_fd,"sss",3,0);//发送sss告诉client把他踢了
            close(overtime_fd);//进行断开操作
            set_remove(&queue[queue_index],overtime_fd);//删除当前队列位置上，集合中的这个fd
            printf("删除后\n");
            set_print(&queue[queue_index]);
            hash[overtime_fd].flag=-1;//hash弃用
            printf("hash[overtime_fd].flag=%d\n",hash[overtime_fd].flag);
            epollDel(epfd,overtime_fd);//不在监听这个netfd
            printf("放弃监听\n");
        }
        char userName[50]={0};//每来一个连接，username是不同的
        char passWord[50]={0};//密码

        for(int i=0;i<ready_len;i++)
        {
            numStack_t stack;
            numStackInit(&stack);
            if(readyset[i].data.fd==sockfd)//有新连接到来 加入监听 
            {
                int netfd=accept(sockfd,NULL,NULL);
                printf("新连接到来 %d\n",netfd);
                printf("i am master i get netfd %d\n",netfd);
                char userName[50]={0};//每来一个连接，username是不同的
                char passWord[50]={0};//密码
                                      //密码验证流程
                while(1)     
                {
                    //首先，先识别是注册还是登录, 1是注册， 2是登录
                    char identify[20] = {0};
                    ssize_t ret1 = recv(netfd, identify,sizeof(identify), MSG_NOSIGNAL);
                    printf("identify=%s\n",identify);
                    if(ret1 <= 0)
                    {
                        printf("识别失败。");
                        continue;
                    }
                    printf("1\n");
                    recv(netfd, userName, sizeof(userName)-1, MSG_NOSIGNAL);
                    printf("1\n");
                    printf("userName=%s\n",userName);
                    int ret= recv(netfd, passWord, sizeof(passWord)-1, MSG_NOSIGNAL);
                    if (ret == -1) {
                        perror("send failed");
                        exit(1);
                    }
                    identify[strcspn(identify, "\n")] = '\0';
                    printf("identify=%s\n",identify);
                    //识别成功
                    if(strcmp("1", identify) == 0)
                    {
                        printf("识别成功，将进行注册。\n");
                        //执行注册程序
                        registerUser(netfd,mysql, userName, passWord);
                        break;
                    }
                    else if(strcmp("2", identify) == 0)
                    {
                        printf("识别成功，将进行登录。\n");
                        //执行登录程序
                        int a=userLogin(netfd,mysql, userName);
                        if(a==-1)
                        {
                            exit(-1);
                            //continue;
                        }
                        break;
                    }
                    else
                    {
                        printf("收到未知标识:%s\n",identify);
                        printf("请重新输入。");
                        continue;
                    }
                }

                //成功 向hash表和循环队列中放东西 
                hash[netfd].flag=1;///////////////////////////////////////
                printf("hash[netfd].flag=%d\n",hash[netfd].flag);
                hash[netfd].queue_location=queue_index;////////////////////////
                printf("hash[netfd].queue_location= %d\n",hash[netfd].queue_location);
                memcpy(hash[netfd].userName,userName,sizeof(userName));//////////////////////////////////////
                set_add(&queue[queue_index],netfd);
                set_print(&queue[queue_index]);
                epollAdd(epfd,netfd);/////////////////////////////

                char sql666[2000]={0};
                printf("查找当前用户根目录位置并压入栈中\n");
                sprintf(sql666,"select * from file_excel where path = '/'  and user_name = '%s' ;",userName);
                mysql_query(mysql,sql666);
                MYSQL_RES *hhh = mysql_store_result(mysql);//得到查询结果                                    
                MYSQL_ROW row=mysql_fetch_row(hhh);//取出查询结果的一行
                int will_in_stack=atoi(row[0]);
                printf("/ in file_excel index is %d\n",will_in_stack);
                pushStack(&stack,will_in_stack);
                //mysql_free_result(hhh);
                printf("user now work_stack init over\n");
                hash[netfd].stack=stack;

                printf("有链接到来，我初始化了他的hash和queue\n");
            }
            else//这个就绪的fd是用户
            {
                for(int j=0;j<100;j++)
                {
                    if(hash[j].flag!=-1&&j==readyset[i].data.fd)
                    {
                        train_t train;
                        recv(j, &train.flag, sizeof(train.flag), 0);
                        //printf("train.flag = %d, train.length = %d, train.data = %s\n", train.flag,train.length,train.data);

                        /*
                         * Package bag;
                         * bag=calloc(1,sizeof(Package));
                         * memcpy(bag.userName,userName,sizoef(userName));
                         * memcpy(bag.fileName,fileName,sizoef(fileName));
                         * bag.netfd=j;
                         * bag.mysql=mysql;
                         * bag.stack=hash[j].stack;
                         */

                        // cd=1,ls=2,remove=5,pwd=6,mkdir=7,rmdir=8
                        if(train.flag == 1||train.flag==2||train.flag ==5 ||train.flag==6||train.flag==7||train.flag==8)
                        {
                            char temp[1024] = {0};
                            //cd 进入某文件夹
                            if(train.flag == 1)
                            {
                                char path[1024] = {0};
                                memset(&train, 0, sizeof(train));
                                recv(j,&train.length, sizeof(train.length), MSG_NOSIGNAL);
                                recv(j,train.data, train.length, MSG_NOSIGNAL);
                                strcpy(path, train.data);
                                printf("----------path; %s\n",path);
                                ssize_t ret = func_cd(path, &stack, mysql, userName);
                                if(ret == 0)
                                {
                                    send(j, "0", 2, MSG_NOSIGNAL);
                                }
                                else
                                {
                                    send(j, "-1", 3, MSG_NOSIGNAL);
                                }
                            }
                            //ls 展示当前目录
                            else if(train.flag == 2)
                            {
                                ssize_t ret =func_ls(temp, &stack, mysql);
                                if(ret == 0)
                                {
                                    send(j, temp, strlen(temp), MSG_NOSIGNAL);
                                }
                                else
                                {
                                    send(j, "-1", 3, MSG_NOSIGNAL);
                                }
                            }
                            //remove 删除文件
                            else if(train.flag == 5)
                            {
                                char fileName[1024] = {0};
                                memset(&train, 0, sizeof(train));
                                recv(j,&train.length, sizeof(train.length), MSG_WAITALL);
                                recv(j,train.data, train.length, MSG_WAITALL);
                                strcpy(fileName, train.data);
                                ssize_t ret =func_remove(fileName, mysql, &stack);
                                if(ret == 0)
                                {
                                    send(j, "0", 2, MSG_NOSIGNAL);
                                }
                                else
                                {
                                    send(j, "-1", 3, MSG_NOSIGNAL);
                                }
                            }
                            //pwd 展示当前路径
                            else if(train.flag == 6)
                            {
                                ssize_t ret =func_pwd(temp,&stack, mysql);
                                if(ret == 0)
                                {
                                    send(j, temp, strlen(temp), MSG_NOSIGNAL);
                                }
                                else
                                {
                                    send(j, "-1", 3, MSG_NOSIGNAL);
                                }
                            }
                            //mkdir 创建文件夹
                            else if(train.flag == 7)
                            {
                                char fileName[1024] = {0};
                                memset(&train, 0, sizeof(train));
                                recv(j, &train.length, sizeof(train.length), MSG_WAITALL);
                                recv(j, train.data, train.length, MSG_WAITALL);
                                strcpy(fileName, train.data);
                                ssize_t ret =func_mkdir(userName,&stack, fileName, mysql);
                                if(ret == 0)
                                {
                                    send(j, "0", 2, MSG_NOSIGNAL);
                                }
                                else
                                {
                                    send(j, "-1", 3, MSG_NOSIGNAL);
                                }
                            }
                            //rmdir 删除文件夹
                            else if(train.flag == 8)
                            {
                                char fileName[1024] = {0};
                                memset(&train, 0, sizeof(train));
                                recv(j, &train.length, sizeof(train.length), MSG_WAITALL);
                                recv(j, train.data, train.length, MSG_WAITALL);
                                strcpy(fileName, train.data);
                                ssize_t ret =func_rmdir(&stack,fileName,mysql);
                                if(ret == 0)
                                {
                                    send(j, "0", 2, MSG_NOSIGNAL);
                                }
                                else
                                {
                                    send(j, "-1", 3, MSG_NOSIGNAL);
                                }

                            }
                        }
                        else//是puts=3，gets=4 主人唤醒一个工人进程进行传输操作
                        {
                            /* char fileName[49]={0};
                               memset(&train, 0, sizeof(train));
                               recv(j, &train.length, sizeof(train.length), MSG_WAITALL);
                               recv(j, train.data, train.length, MSG_WAITALL);
                               Package bag;
                               memcpy(bag.userName,userName,sizeof(userName));
                               memcpy(bag.fileName,fileName,sizeof(fileName));
                               bag.netfd=j;
                               bag.mysql=mysql;
                               bag.stack=hash[j].stack;
                               pthread_t tid;*/

                            //我方gets 客户端puts
                            if(train.flag == 3)
                            {
                                char fileName[49]={0};
                                memset(&train,0,sizeof(train));
                                recv(j, &train.length, sizeof(train.length), MSG_WAITALL);
                                recv(j, train.data, train.length,MSG_WAITALL);
                                Package bag;
                                memcpy(bag.userName,userName,sizeof(userName));
                                memcpy(bag.fileName,fileName,sizeof(fileName));
                                bag.netfd=j;
                                bag.mysql=mysql;
                                bag.stack=hash[j].stack;
                                pthread_t tid;
                                pthread_create(&tid,NULL,worker,&bag);
                            }
                            else if(train.flag == 4)
                            {
                                char fileName[49]={0};
                                memset(&train,0,sizeof(train));
                                recv(j, &train.length, sizeof(train.length), MSG_WAITALL);
                                recv(j, train.data, train.length,MSG_WAITALL);
                                Package bag;
                                memcpy(bag.userName,userName,sizeof(userName));
                                memcpy(bag.fileName,fileName,sizeof(fileName));
                                bag.netfd=j;
                                bag.mysql=mysql;
                                bag.stack=hash[j].stack;
                                pthread_t tid;
                                pthread_create(&tid,NULL,worker,&bag);
                            }
                        }  
                        //这个fd触发了，那么将循环队列中的这个fd位置移动到当前queue_index
                        int fd_in_queue_loc=hash[j].queue_location;////////////////////////////在队列中位置
                        printf("更新了fd=%d的循环队列位置和hash表\n",j);
                        printf("fd_in_queue_loc=%d\n",fd_in_queue_loc);
                        set_remove(&queue[fd_in_queue_loc],j); /////////////////////////// 删去 
                        printf("删去 \n");
                        set_print(&queue[fd_in_queue_loc]);
                        set_add(&queue[queue_index],j);//////////////////////////////////新增 
                        printf("新增 \n");
                        set_print(&queue[queue_index]);
                        hash[j].queue_location=queue_index;////////////////////////////////////
                        printf("hash[j].queue_location = %d\n",queue_index);
                    }//if
                }//for fd
            }//fd是否就绪
             //针对当前queue_index中所有fd全部断开连接   踢人
        }//遍历ready_set
    }//while(1)死循环
    return 0;
}
